package com.skillworld.webapp.model.user;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;
import org.springframework.stereotype.Repository;

import com.skillworld.webapp.model.team.Team;
import com.skillworld.webapp.model.tutorial.Tutorial;
import com.skillworld.webapp.model.tutorial.Tutorial.Status;
import com.skillworld.webapp.model.util.dao.GenericDaoHibernate;

import es.udc.pojo.modelutil.exceptions.InstanceNotFoundException;

@Repository("UserDao")
public class UserDaoHibernate 
        extends GenericDaoHibernate<User, Long> 
        implements UserDao {

    public boolean existsByNickname(String nickname)
    {       
        return getSession().createQuery(
                  "SELECT u.userId FROM User u " +
                  "WHERE UPPER(u.nickname) = UPPER(:nick)"
                  ).setParameter("nick", nickname) 
                   .uniqueResult() != null;
    }
    
    public boolean existsByFacebookId(String facebookId)
    {
        return getSession().createQuery(
                "SELECT u.userId FROM User u " +
                "WHERE u.facebookId = :fbId"
                ).setParameter("fbId", facebookId) 
                 .uniqueResult() != null;   
    }
    
    private Criteria buildFindByNicknamePrefixQuery(String nickPrefix,
                        Long inTeam, Long notInTeam, List<Long> exclude)
    {
        Criteria q = getSession().createCriteria(User.class);
        
        q.add(Restrictions.ilike("nickname", nickPrefix, MatchMode.START));
        
        if (exclude != null)
        { q.add(Restrictions.not(Restrictions.in("userId", exclude))); }
        
        if (inTeam != null)
        { 
            q.createAlias("teams", "t")
             .add(Restrictions.eq("t.teamId", inTeam));
        }
        
        if (notInTeam != null)
        {
            DetachedCriteria teamMembers = 
                DetachedCriteria.forClass(User.class)
                    .createAlias("teams", "t")
                    .add(Restrictions.eq("t.teamId", notInTeam))
                    .setProjection(Property.forName("userId"));    
                
            q.add(Subqueries.propertyNotIn("userId", teamMembers));
        }
        
        return q;
    }
    
	@SuppressWarnings("unchecked")
    public List<User> findByNicknamePrefix(String nickPrefix, 
                Long inTeam, Long notInTeam, List<Long> exclude, 
                int startIndex, int count)
    {		
	    
		return(List<User>) 
		    buildFindByNicknamePrefixQuery(nickPrefix, inTeam, notInTeam, exclude)
		                        .addOrder(Order.asc("nickname").ignoreCase())
		                        .setFirstResult(startIndex)
		                        .setMaxResults(count)
		                        .list();
	}
	
    public long numberOfUsersByNicknamePrefix(String nickPrefix, 
                Long inTeam, Long notInTeam, List<Long> exclude)
	{
	    return(Long) 
	        buildFindByNicknamePrefixQuery(nickPrefix, inTeam, notInTeam, exclude)
	                        .setProjection(Projections.rowCount())
	                        .uniqueResult();
    }
	
	public User findByNickname(String nickname)
	        throws InstanceNotFoundException 
	{
        User user =(User)
            getSession().createQuery(
                             "SELECT u FROM User u " +
                             "WHERE UPPER(u.nickname) = UPPER(:nick)"
                             ).setParameter("nick", nickname) 
                              .uniqueResult();
        
        if (user == null)
        {
            throw new InstanceNotFoundException(nickname, User.class.getName());
        }
        
        return user;
	}

	public User findByFacebookId(String facebookId)
	        throws InstanceNotFoundException
	{
		User user =(User) 
		    getSession().createQuery(
		                        "SELECT u FROM User u " +
        		                "WHERE u.facebookId = :faceId"
		                        ).setParameter("faceId", facebookId)
		                         .uniqueResult();
		
		if (user == null)
		{
		    throw new InstanceNotFoundException(facebookId, User.class.getName());
		}
		
		return user;
	}

	@SuppressWarnings("unchecked")
    public List<User> topUsers(int count)
	{
        return(List<User>) getSession().createQuery(
                                "SELECT u FROM User u " +
                                "ORDER BY u.totalPoints DESC")
                                .setFirstResult(0)
                                .setMaxResults(count)
                                .list();
	}
	
/*
    public int numberOfTutorialsAccepted(long userId)
    { 
        return getSession().createQuery(
                    "SELECT count(t) FROM Tutorial t " +
                    "WHERE t.author.userId = :userId and (t.status = :statusA or t.status=:statusV)")
                        .setParameter("userId", userId)
                        .setParameter("statusA", Status.ACCEPTED)
                        .setParameter("statusV", Status.VALIDATED)
                        .list()
                        .size();
    }

    public int numberOfTutorialsValidated(long userId)
    { 
        return getSession().createQuery(
                    "SELECT t FROM Tutorial t " +
                    "WHERE t.validator.userId = :userId ")
                        .setParameter("userId", userId)
                        .list()
                        .size();
    }
*/
}
